#ifndef __MCS__FUNCTION_FACTORY_DETAILS__HPP
#define __MCS__FUNCTION_FACTORY_DETAILS__HPP

#include "Function.hpp"

template<class T> struct TypeTraits;

template<> struct TypeTraits<bool>
{
	static Value::DataType const type = Value::Bool;
	typedef BoolValue ValueClass;

	static bool castFromValueObject(ptr<Value> val)
	{
		return val.dynamicCast<BoolValue>()->value();
	}
};

template<> struct TypeTraits<int>
{
	static Value::DataType const type = Value::Int;
	typedef IntValue ValueClass;

	static int castFromValueObject(ptr<Value> val)
	{
		return val.dynamicCast<IntValue>()->value();
	}
};

template<> struct TypeTraits<String>
{
	static Value::DataType const type = Value::Str;
	typedef StrValue ValueClass;

	static String castFromValueObject(ptr<Value> val)
	{
		return val.dynamicCast<StrValue>()->value();
	}
};

template<> struct TypeTraits<String const &> : public TypeTraits<String> {};

template<class T> class FunctionT;

template<class R> class FunctionT<R(*)()> : public Function
{
public:
	FunctionT(String const & name, R (*func)())
		: Function(name, TypeTraits<R>::type)
		, func_(func)
	{
	}

	virtual ptr<Value> invoke(std::vector< ptr<Value> > const & args) const
	{
		R retVal = (*func_)();
		return new typename TypeTraits<R>::ValueClass(retVal);
	}
private:
	R (*func_)();
};

template<class R, class T1> class FunctionT<R(*)(T1)> : public Function
{
public:
	FunctionT(String const & name, R (*func)(T1))
		: Function(name, TypeTraits<R>::type)
		, func_(func)
	{
		addArgType(TypeTraits<T1>::type);
	}

	virtual ptr<Value> invoke(std::vector< ptr<Value> > const & args) const
	{
		R retVal = (*func_)(
			TypeTraits<T1>::castFromValueObject(args.at(0))
		);
		return new typename TypeTraits<R>::ValueClass(retVal);
	}
private:
	R (*func_)(T1);
};

template<class R, class T1, class T2> class FunctionT<R(*)(T1,T2)> : public Function
{
public:
	FunctionT(String const & name, R (*func)(T1,T2))
		: Function(name, TypeTraits<R>::type)
		, func_(func)
	{
		addArgType(TypeTraits<T1>::type);
		addArgType(TypeTraits<T2>::type);
	}

	virtual ptr<Value> invoke(std::vector< ptr<Value> > const & args) const
	{
		R retVal = (*func_)(
			TypeTraits<T1>::castFromValueObject(args.at(0)),
			TypeTraits<T2>::castFromValueObject(args.at(1))
		);
		return new typename TypeTraits<R>::ValueClass(retVal);
	}
private:
	R (*func_)(T1,T2);
};

template<class R, class T1, class T2, class T3> class FunctionT<R(*)(T1,T2,T3)> : public Function
{
public:
	FunctionT(String const & name, R (*func)(T1,T2,T3))
		: Function(name, TypeTraits<R>::type)
		, func_(func)
	{
		addArgType(TypeTraits<T1>::type);
		addArgType(TypeTraits<T2>::type);
		addArgType(TypeTraits<T3>::type);
	}

	virtual ptr<Value> invoke(std::vector< ptr<Value> > const & args) const
	{
		R retVal = (*func_)(
			TypeTraits<T1>::castFromValueObject(args.at(0)),
			TypeTraits<T2>::castFromValueObject(args.at(1)),
			TypeTraits<T3>::castFromValueObject(args.at(2))
		);
		return new typename TypeTraits<R>::ValueClass(retVal);
	}
private:
	R (*func_)(T1,T2,T3);
};

template<class R, class T1, class T2, class T3, class T4> class FunctionT<R(*)(T1,T2,T3,T4)> : public Function
{
public:
	FunctionT(String const & name, R (*func)(T1,T2,T3,T4))
		: Function(name, TypeTraits<R>::type)
		, func_(func)
	{
		addArgType(TypeTraits<T1>::type);
		addArgType(TypeTraits<T2>::type);
		addArgType(TypeTraits<T3>::type);
		addArgType(TypeTraits<T4>::type);
	}

	virtual ptr<Value> invoke(std::vector< ptr<Value> > const & args) const
	{
		R retVal = (*func_)(
			TypeTraits<T1>::castFromValueObject(args.at(0)),
			TypeTraits<T2>::castFromValueObject(args.at(1)),
			TypeTraits<T3>::castFromValueObject(args.at(2)),
			TypeTraits<T4>::castFromValueObject(args.at(3))
		);
		return new typename TypeTraits<R>::ValueClass(retVal);
	}
private:
	R (*func_)(T1,T2,T3,T4);
};

template<class R, class T1, class T2, class T3, class T4, class T5> class FunctionT<R(*)(T1,T2,T3,T4,T5)> : public Function
{
public:
	FunctionT(String const & name, R (*func)(T1,T2,T3,T4,T5))
		: Function(name, TypeTraits<R>::type)
		, func_(func)
	{
		addArgType(TypeTraits<T1>::type);
		addArgType(TypeTraits<T2>::type);
		addArgType(TypeTraits<T3>::type);
		addArgType(TypeTraits<T4>::type);
		addArgType(TypeTraits<T5>::type);
	}

	virtual ptr<Value> invoke(std::vector< ptr<Value> > const & args) const
	{
		R retVal = (*func_)(
			TypeTraits<T1>::castFromValueObject(args.at(0)),
			TypeTraits<T2>::castFromValueObject(args.at(1)),
			TypeTraits<T3>::castFromValueObject(args.at(2)),
			TypeTraits<T4>::castFromValueObject(args.at(3)),
			TypeTraits<T5>::castFromValueObject(args.at(4))
		);
		return new typename TypeTraits<R>::ValueClass(retVal);
	}
private:
	R (*func_)(T1,T2,T3,T4,T5);
};

#endif //__MCS__FUNCTION_FACTORY_DETAILS__HPP